home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / DIConfig / populate.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  13.4 KB  |  497 lines

  1. //-----------------------------------------------------------------------------
  2. // File: populate.cpp
  3. //
  4. // Desc: This file contains the population functions.  These are all
  5. //       accessed through PopulateAppropriately().  That function creates
  6. //       views & controls based on the type of the device that the passed
  7. //       DeviceUI represents.
  8. //
  9. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  10. //-----------------------------------------------------------------------------
  11.  
  12. #include "common.hpp"
  13.  
  14.  
  15. // these functions are internal to this filed, called only by
  16. // PopulateAppropriately().
  17. HRESULT PopulateViaGetImageInfo(CDeviceUI &ui);
  18. HRESULT PopulateFromImageInfoHeader(CDeviceUI &ui, const DIDEVICEIMAGEINFOHEADERW &);
  19. HRESULT PopulateListView(CDeviceUI &ui);
  20. HRESULT PopulateErrorView(CDeviceUI &ui);
  21.  
  22.  
  23. // Clears the entire passed DeviceUI, then fills it with views and
  24. // controls based on device type.  Tries to gaurantee that there will
  25. // be at least one view.
  26. HRESULT PopulateAppropriately(CDeviceUI &ui)
  27. {
  28.     HRESULT hr = S_OK;
  29.  
  30.     // first empty the ui
  31.     ui.Unpopulate();
  32.  
  33.     // get device type
  34.     DWORD dwdt = ui.m_didi.dwDevType;
  35.     DWORD dwType = (DWORD)(LOBYTE(LOWORD(dwdt)));
  36.     DWORD dwSubType = (DWORD)(HIBYTE(LOWORD(dwdt)));
  37.  
  38.     // based on type...
  39.     switch (dwType)
  40.     {
  41.         default:
  42.             // unless its a type we don't ever want views for,
  43.             // populate via the GetImageInfo() API
  44.             hr = PopulateViaGetImageInfo(ui);
  45.             if (SUCCEEDED(hr) && ui.GetNumViews() > 0)
  46.                 return hr;
  47.  
  48.             // if it failed or resulted in nothing,
  49.             // clear anything that might've been added
  50.             ui.Unpopulate();
  51.  
  52.             // intentional fallthrough
  53.  
  54.         case DI8DEVTYPE_MOUSE:
  55.         case DI8DEVTYPE_KEYBOARD:
  56.             // for types that we don't ever want views for
  57.             // we populate the list view without trying the above
  58.             hr = PopulateListView(ui);
  59.             
  60.             // if we still failed or don't have any views,
  61.             // populate with error message view
  62.             if (FAILED(hr) || ui.GetNumViews() < 1)
  63.             {
  64.                 // empty
  65.                 ui.Unpopulate();
  66.  
  67.                 // show error message
  68.                 hr = PopulateErrorView(ui);
  69.             }
  70.  
  71.             // this function should guarantee success
  72.             assert(!FAILED(hr));
  73.  
  74.             return hr;
  75.     }
  76. }
  77.  
  78. // Calls the GetImageInfo() API to get the view images and controls
  79. // for the entire device, and returns a failure if there's the
  80. // slightest problem (if GII() fails, or if an image fails to load,
  81. // etc.)
  82. HRESULT PopulateViaGetImageInfo(CDeviceUI &ui)
  83. {
  84.     if (!ui.m_lpDID)
  85.         return E_FAIL;
  86.  
  87.     HRESULT hr = S_OK;
  88.  
  89.     DIDEVICEIMAGEINFOHEADERW m_diImgInfoHdr;
  90.     LPDIDEVICEIMAGEINFOW &lprgdiImgData = m_diImgInfoHdr.lprgImageInfoArray;
  91.  
  92.     ZeroMemory( &m_diImgInfoHdr, sizeof(DIDEVICEIMAGEINFOHEADERW) );
  93.     m_diImgInfoHdr.dwSize = sizeof(DIDEVICEIMAGEINFOHEADERW);
  94.     m_diImgInfoHdr.dwSizeImageInfo = sizeof(DIDEVICEIMAGEINFOW);
  95.  
  96.     // Retrieve the required buffer size.
  97.     hr = ui.m_lpDID->GetImageInfo( &m_diImgInfoHdr );
  98.     if (FAILED(hr))
  99.     {
  100.         etrace1(_T("GetImageInfo() failed while trying to get required buffer size.  hr = 0x%08x\n"), hr);
  101.         return E_FAIL;
  102.     }
  103.  
  104.     // Allocate the buffer.
  105.     lprgdiImgData = (LPDIDEVICEIMAGEINFOW) malloc( (size_t)
  106.         (m_diImgInfoHdr.dwBufferSize = m_diImgInfoHdr.dwBufferUsed) );
  107.     if (lprgdiImgData == NULL)
  108.     {
  109.         etrace1(_T("Could not allocate buffer of size %d.\n"), m_diImgInfoHdr.dwBufferSize);
  110.         return E_FAIL;
  111.     }
  112.  
  113.     m_diImgInfoHdr.lprgImageInfoArray = lprgdiImgData;
  114.  
  115.     // Get the display info.
  116.     hr = ui.m_lpDID->GetImageInfo( &m_diImgInfoHdr );
  117.     if (FAILED(hr))
  118.     {
  119.         etrace1(_T("GetImageInfo() failed trying to get image info.  hr = 0x%08x\n"), hr);
  120.         free(lprgdiImgData);
  121.         lprgdiImgData = NULL;
  122.         return E_FAIL;
  123.     }
  124.  
  125.     // actually populate now
  126.     hr = PopulateFromImageInfoHeader(ui, m_diImgInfoHdr);
  127.     if (FAILED(hr))
  128.         return hr;
  129.  
  130.     // free stuff
  131.     free(lprgdiImgData);
  132.     lprgdiImgData = NULL;
  133.  
  134.     return S_OK;
  135. }
  136.  
  137. // basically does the work for the above function after the header
  138. // is actually retrieved
  139. HRESULT PopulateFromImageInfoHeader(CDeviceUI &ui, const DIDEVICEIMAGEINFOHEADERW &dih)
  140. {
  141.     if (dih.dwSizeImageInfo != sizeof(DIDEVICEIMAGEINFOW))
  142.     {
  143.         etrace(_T("dwSizeImageInfo Incorrect.\n"));
  144.         assert(0);
  145.         return E_FAIL;
  146.     }
  147.     DWORD dwNumElements = dih.dwBufferUsed / dih.dwSizeImageInfo;
  148.     if (dwNumElements * dih.dwSizeImageInfo != dih.dwBufferUsed
  149.         || dih.dwBufferUsed < dih.dwBufferSize)
  150.     {
  151.         etrace(_T("Could not confidently calculate dwNumElements.\n"));
  152.         assert(0);
  153.         return E_FAIL;
  154.     }
  155.  
  156.     DWORD i;
  157.  
  158.     bidirlookup<DWORD, int> offset_view;
  159.  
  160.     {
  161.         for (i = 0; i < dwNumElements; i++)
  162.             if (dih.lprgImageInfoArray[i].dwFlags & DIDIFT_CONFIGURATION)
  163.             {
  164.                 LPDIDEVICEIMAGEINFOW lpInfoBase = dih.lprgImageInfoArray;
  165.                 DWORD index = i;
  166.                 {
  167.                     if (lpInfoBase == NULL)
  168.                     {
  169.                         etrace(_T("lpInfoBase NULL\n"));
  170.                         return E_FAIL;
  171.                     }
  172.  
  173.                     DIDEVICEIMAGEINFOW &info = lpInfoBase[index];
  174.                     DWORD dwOffset = index;
  175.  
  176.                     // add view info to array
  177.                     CDeviceView *pView = ui.NewView();
  178.                     if (!pView)
  179.                     {
  180.                         etrace(_T("Could not create new view.\n"));
  181.                         return E_FAIL;
  182.                     }
  183.                     int nView = pView->GetViewIndex();
  184.  
  185.                     // set view's imagepath
  186.                     if (!info.tszImagePath)
  187.                     {
  188.                         etrace(_T("No image path.\n"));
  189.                         return E_FAIL;
  190.                     }
  191.                     LPTSTR tszImagePath = AllocLPTSTR(info.tszImagePath);
  192.                     if (!tszImagePath)
  193.                     {
  194.                         etrace(_T("Could not copy image path.\n"));
  195.                         return E_FAIL;
  196.                     }
  197.  
  198.                     // set the view's image path
  199.                     pView->SetImagePath(tszImagePath);
  200.  
  201.                     // create bitmap from path
  202.                     LPDIRECT3DSURFACE9 lpSurf3D = ui.m_uig.GetSurface3D();
  203.                     CBitmap *pbm = CBitmap::CreateViaD3DX(tszImagePath, lpSurf3D);
  204.                     free(tszImagePath);
  205.                     tszImagePath = NULL;
  206.                     if (lpSurf3D)
  207.                     {
  208.                         lpSurf3D->Release();  // Need to free the surface instance after we are done as AddRef() was called earlier.
  209.                         lpSurf3D = NULL;
  210.                     }
  211.                     if (!pbm)
  212.                     {
  213.                         etrace(_T("Could not create image from path.\n"));
  214.                         return E_FAIL;
  215.                     }
  216.  
  217.                     // set the view's image
  218.                     assert(pbm != NULL);
  219.                     pView->SetImage(pbm);    // setimage steals the bitmap pointer
  220.                     assert(pbm == NULL);
  221.  
  222.                     // add conversion from offset to view
  223.                     offset_view.add(dwOffset, nView);
  224.                 }
  225.             }
  226.     }
  227.  
  228.     {
  229.         for (i = 0; i < dwNumElements; i++)
  230.         {
  231.             DWORD dwFlags = dih.lprgImageInfoArray[i].dwFlags;
  232.  
  233.             if (dwFlags & DIDIFT_OVERLAY)
  234.             {
  235.                 LPDIDEVICEIMAGEINFOW lpInfoBase = dih.lprgImageInfoArray;
  236.                 DWORD index = i;
  237.                 {
  238.                     if (lpInfoBase == NULL)
  239.                     {
  240.                         etrace(_T("lpInfoBase NULL\n"));
  241.                         return E_FAIL;
  242.                     }
  243.  
  244.                     DIDEVICEIMAGEINFOW &info = lpInfoBase[index];
  245.  
  246.                     int nViewIndex = 0;
  247.                     
  248.                     if (!offset_view.getright(info.dwViewID, nViewIndex))
  249.                     {
  250.                         etrace(_T("Could not get view index\n"));
  251.                         return E_FAIL;
  252.                     }
  253.  
  254.                     if (nViewIndex < 0 || nViewIndex >= ui.GetNumViews())
  255.                     {
  256.                         etrace1(_T("Invalid view index %d\n"), nViewIndex);
  257.                         return E_FAIL;
  258.                     }
  259.  
  260.                     CDeviceView *pView = ui.GetView(nViewIndex);
  261.                     if (!pView)
  262.                     {
  263.                         etrace1(_T("Couldn't get device view!\n"), nViewIndex);
  264.                         return E_FAIL;
  265.                     }
  266.  
  267.                     CDeviceControl *pControl = pView->NewControl();
  268.                     if (!pControl)
  269.                     {
  270.                         etrace1(_T("Couldn't get device control!\n"), nViewIndex);
  271.                         return E_FAIL;
  272.                     }
  273.                     int nControl = pControl->GetControlIndex();
  274.  
  275.                     pControl->SetObjID(info.dwObjID);
  276.                     pControl->SetLinePoints(int(info.dwcValidPts), info.rgptCalloutLine);
  277.                     pControl->SetCalloutMaxRect(info.rcCalloutRect);
  278.                     pControl->SetAlignment(info.dwTextAlign);
  279.                     if (info.tszImagePath)
  280.                     {
  281.                         LPTSTR tszOverlayPath = AllocLPTSTR(info.tszImagePath);
  282.                         if (tszOverlayPath)
  283.                         {
  284.                             pControl->SetOverlayPath(tszOverlayPath);
  285.                             free(tszOverlayPath);
  286.                             tszOverlayPath = NULL;
  287.                         }
  288.                     }
  289.                     pControl->SetOverlayRect(info.rcOverlay);
  290.                     pControl->Init();
  291.                 }
  292.             }
  293.         }
  294.     }
  295.  
  296.     return S_OK;
  297. }
  298.  
  299. // Enumerates the controls on the device and creates one big list
  300. // view for the device.  Fails if it can't enumerate for some reason.
  301. HRESULT PopulateListView(CDeviceUI &ui)
  302. {
  303.     int i;
  304.     HRESULT hr = S_OK;
  305.  
  306.     // we must have the device interface
  307.     if (!ui.m_lpDID)
  308.         return E_FAIL;
  309.  
  310.     // create one view
  311.     CDeviceView *pView = ui.NewView();
  312.     if (!pView)
  313.         return E_FAIL;
  314.  
  315.     // enable scrolling on it
  316.     pView->EnableScrolling();
  317.  
  318.     // get list of controls
  319.     DIDEVOBJSTRUCT os;
  320.     hr = FillDIDeviceObjectStruct(os, ui.m_lpDID);
  321.     if (FAILED(hr))
  322.         return hr;
  323.  
  324.     // if there aren't any, fail
  325.     int n = os.nObjects;
  326.     if (n < 1)
  327.         return E_FAIL;
  328.  
  329.     // run through and create a text for every control to 
  330.     // get the sizing
  331.     POINT origin = {0, 0};
  332.     SIZE max = {0, 0};
  333.     for (i = 0; i < n; i++)
  334.     {
  335.         LPTSTR tszName = AllocLPTSTR(os.pdoi[i].tszName);
  336.         CDeviceViewText *pText = pView->AddText(
  337.             (HFONT)ui.m_uig.GetFont(UIE_DEVOBJ),
  338.             ui.m_uig.GetTextColor(UIE_DEVOBJ),
  339.             ui.m_uig.GetBkColor(UIE_DEVOBJ),
  340.             origin,
  341.             tszName);
  342.         free(tszName);
  343.         if (!pText)
  344.                 {
  345.             return E_FAIL;
  346.                 }
  347.         SIZE tsize = GetRectSize(pText->GetRect());
  348.         if (tsize.cx > max.cx)
  349.             max.cx = tsize.cx;
  350.         if (tsize.cy > max.cy)
  351.             max.cy = tsize.cy;
  352.     }
  353.  
  354.     // Find out if we should use one column or two columns if this is a kbd device.
  355.     BOOL bUseTwoColumns = FALSE;
  356.     if (LOBYTE(LOWORD(ui.m_didi.dwDevType)) == DI8DEVTYPE_KEYBOARD &&
  357.         ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) - max.cx >= MINLISTVIEWCALLOUTWIDTH)
  358.         bUseTwoColumns = TRUE;
  359.  
  360.     // calculate max callout height based on the two possible fonts
  361.     int cmaxh = 0,
  362.         ch = 2 + GetTextHeight((HFONT)ui.m_uig.GetFont(UIE_CALLOUT)),
  363.         chh = 2 + GetTextHeight((HFONT)ui.m_uig.GetFont(UIE_CALLOUTHIGH));
  364.     if (ch > cmaxh)
  365.         cmaxh = ch;
  366.     if (chh > cmaxh)
  367.         cmaxh = chh;
  368.  
  369.     // calculate the bigger of text/callout
  370.     int h = 0;
  371.     if (cmaxh > h)
  372.         h = cmaxh;
  373.     if (max.cy > h)
  374.         h = max.cy;
  375.  
  376.     // calculate vertical offsets of text/callout within max spacing
  377.     int to = (h - max.cy) / 2,
  378.         co = (h - cmaxh) / 2;
  379.  
  380.     // max width for text is half of the view window
  381.     if (max.cx > ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1))
  382.         max.cx = ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1);
  383.  
  384.     // go back through all the controls and place the text while
  385.     // creating the corresponding callouts
  386.     HDC hDC = CreateCompatibleDC(NULL);
  387.     CPaintHelper ph(ui.m_uig, hDC);
  388.     ph.SetElement(UIE_DEVOBJ);
  389.     int at = 0;  // Start at second row since first row is used for header. Also half row spacing
  390.     for (i = 0; i < n; i++)
  391.     {
  392.         // reposition the text
  393.         CDeviceViewText *pText = pView->GetText(i);
  394.         if (!pText)
  395.         {
  396.             DeleteDC(hDC);
  397.             return E_FAIL;
  398.         }
  399.  
  400.         SIZE s = GetRectSize(pText->GetRect());
  401.         if (bUseTwoColumns)
  402.         {
  403.             int iXOffset = i & 1 ? ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) : 0;
  404.  
  405.             RECT rect = {iXOffset,
  406.                          at + to,
  407.                          max.cx + iXOffset,
  408.                          at + to + s.cy};
  409.             // Get the rectangle that is actually used.
  410.             RECT adjrect = rect;
  411.             if (hDC)
  412.             {
  413.                 DrawText(hDC, pText->GetText(), -1, &adjrect, DT_NOPREFIX|DT_CALCRECT);
  414.                 // If the rect actually used is smaller than the space available, use the smaller rect and align to right.
  415.                 if (adjrect.right < rect.right)
  416.                     rect.left += rect.right - adjrect.right;
  417.             }
  418.             pText->SetRect(rect);
  419.         }
  420.         else
  421.         {
  422.             RECT rect = {0, at + to, max.cx /*> ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) ?
  423.                          ((g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1) : max.cx*/, at + to + s.cy};
  424.             pText->SetRect(rect);
  425.         }
  426.  
  427.         // create the control
  428.         CDeviceControl *pControl = pView->NewControl();
  429.         if (!pControl)
  430.         {
  431.             DeleteDC(hDC);
  432.             return E_FAIL;
  433.         }
  434.  
  435.         // position it
  436.         RECT rect = {max.cx + 10, at, (g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1, at + h};
  437.         // If single column, extend callout all the way to right end of view window
  438.         if (!bUseTwoColumns)
  439.             rect.right = g_sizeImage.cx - DEFAULTVIEWSBWIDTH;
  440.         // If this is a keyboard, move to right column on odd numbered controls.
  441.         if (bUseTwoColumns && (i & 1))
  442.         {
  443.             rect.left += (g_sizeImage.cx - DEFAULTVIEWSBWIDTH) >> 1;
  444.             rect.right = g_sizeImage.cx - DEFAULTVIEWSBWIDTH;
  445.         }
  446.         pControl->SetCalloutMaxRect(rect);
  447.  
  448.         // align it
  449.         pControl->SetAlignment(CAF_LEFT);
  450.  
  451.         // set approp offset
  452.         pControl->SetObjID(os.pdoi[i].dwType);
  453.  
  454.         // init it
  455.         pControl->Init();
  456.  
  457.         // go to next y coord
  458.         // If this is a keyboard, then only increase y when we are moving to even numbered controls.
  459.         if (!bUseTwoColumns || (i & 1))
  460.             at += h;
  461.     }
  462.     DeleteDC(hDC);
  463.  
  464.     // make selection/thumb images (just for kicks)
  465.     pView->MakeMissingImages();
  466.     
  467.     // calculate view dimensions (for scrolling)
  468.     pView->CalcDimensions();
  469.  
  470.     return S_OK;
  471. }
  472.  
  473. // Creates a single view with an error message.  Should not fail.
  474. HRESULT PopulateErrorView(CDeviceUI &ui)
  475. {
  476.     // create the new view
  477.     CDeviceView *pView = ui.NewView();
  478.     if (!pView)
  479.         return E_FAIL;
  480.  
  481.     // add text objects containing error message
  482.     pView->AddWrappedLineOfText(
  483.         (HFONT)ui.m_uig.GetFont(UIE_ERRORHEADER),
  484.         ui.m_uig.GetTextColor(UIE_ERRORHEADER),
  485.         ui.m_uig.GetBkColor(UIE_ERRORHEADER),
  486.         _T("Error!"));
  487.     pView->AddWrappedLineOfText(
  488.         (HFONT)ui.m_uig.GetFont(UIE_ERRORMESSAGE),
  489.         ui.m_uig.GetTextColor(UIE_ERRORMESSAGE),
  490.         ui.m_uig.GetBkColor(UIE_ERRORMESSAGE),
  491.         _T("Could not create views for device."));
  492.  
  493.     pView->MakeMissingImages();
  494.  
  495.     return S_OK;
  496. }
  497.